/*
 *  Copyright (c) 2010 Mathew Hall.
 *  All rights reserved.
 * 
 *  Redistribution and use in source and binary forms, with or
 *  without modification, are permitted provided that the following conditions
 *  are met:
 * 
 *  Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above
 *  copyright notice, this list of conditions and the following
 *  disclaimer in the documentation and/or other materials provided
 *  with the distribution.
 *  Neither the name of the University of Sheffield nor the names of its
 *  contributors may be used to endorse or promote products derived
 *  from this software without specific prior written permission.
 * 
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 *  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 *  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 *  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 *  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package main;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.Properties;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

/**
 *
 * @author Mathew Hall
 */
public class RunFile {

    public static ArrayList<Experiment> run(String filename) throws IOException {
        return run(new File(filename));
    }

    public static ArrayList<Experiment> run(File file) throws IOException {
        Properties toRun = new Properties();
        toRun.load(new FileInputStream(file));

        String expts = toRun.getProperty("names");
        String[] files = expts.split(",");

        String fitness = toRun.getProperty("fitness_function");
        
        //rewrite old fitness function names to new ones
        fitness = fitness.replaceAll("searcher.fitnessfunctions", "search.fitnessfunctions");

        int popSize = Integer.parseInt(toRun.getProperty("population"));

        int generations = Integer.parseInt(toRun.getProperty("generations"));

        int chromosomeSize = Integer.parseInt(toRun.getProperty("chromosome_size"));

        int iterations = 1;
        try{
            iterations = Integer.parseInt(toRun.getProperty("iterations"));
        }catch(NumberFormatException e){
            iterations = 1;
        }

        String experiment_name = toRun.getProperty("experiment_name","");

        String output_dot_path = toRun.getProperty("output_dot_path", null);

        String output_sil_path = toRun.getProperty("output_sil_path", null);

        String output_fit_path = toRun.getProperty("output_fitness_path", null);

        String output_evals_path = toRun.getProperty("output_evaluations_path", null);

        String output_serial_path = toRun.getProperty("output_serial_path", null);

        String gene_type = toRun.getProperty("gene_type", "IntegerInstructionGene");
        
        String search_type = toRun.getProperty("search_type", "GA");

	int fitness_budget = Integer.parseInt(toRun.getProperty("fitness_budget", "-1"));

        //TODO: these are reading as false even with a path and flag true
        /*
        boolean output_dot = Boolean.getBoolean(toRun.getProperty("output_dot", "true")) && output_dot_path != null;
        boolean output_sil = Boolean.getBoolean(toRun.getProperty("output_sil", "true")) && output_sil_path != null;
        boolean output_fitness = Boolean.getBoolean(toRun.getProperty("output_fitness", "true")) && output_fit_path != null;
        boolean output_serialised = Boolean.getBoolean(toRun.getProperty("output_fitness", "true")) && output_serial_path != null;
        */
        boolean output_dot =  output_dot_path != null;
        boolean output_sil = output_sil_path != null;
        boolean output_fitness = output_fit_path != null;
        boolean output_serialised = output_serial_path != null;
        boolean output_evals = output_evals_path != null;
        String output_dot_format = toRun.getProperty("output_dot_format", "%f%n%D");

        String output_sil_format = toRun.getProperty("output_sil_format", "%f%n%D");

        String output_fit_format = toRun.getProperty("output_fitness_format", "%f%n%D");

        String output_serial_format = toRun.getProperty("output_serial_format", "%f%n%D");

        String ouput_evals_format = toRun.getProperty("output_evaluations_format", "%f%n%D");

        String sNumThreads = toRun.getProperty("numthreads", new Integer(Runtime.getRuntime().availableProcessors()).toString());

        int numThreads = Integer.parseInt(sNumThreads);


        ArrayList<Experiment> toDo = new ArrayList<Experiment>();
        for (String f : files) {

            f.trim();

            if(f.isEmpty()) continue;
            String noExtension = f.replace(".dot", "");
            int lastSlash = noExtension.lastIndexOf(File.separator)+1;
            noExtension = noExtension.substring(lastSlash, noExtension.length());

            File input = new File(f);

            for(int current = 1; current <= iterations; current++){
                Experiment newE = new Experiment(input, fitness, numThreads);

                if (output_sil) {
                    newE.setSiloutpath(new File(output_sil_path + "/" + formatName(output_sil_format,noExtension,current,experiment_name,gene_type) + ".sil"));
                }
                if (output_dot) {
                    newE.setDotoutpath(new File(output_dot_path + "/" + formatName(output_dot_format,noExtension,current,experiment_name,gene_type) + ".dot"));
                }
                if (output_fitness) {
                    newE.setFitnessoutpath(new File(output_fit_path + "/" + formatName(output_fit_format,noExtension,current,experiment_name,gene_type) + ".fitness"));
                }
                if (output_serialised) {
                    newE.setClusteroutpath(new File(output_serial_path + "/" + formatName(output_serial_format,noExtension,current,experiment_name,gene_type) + ".ClusterHead"));
                }
                if (output_evals) {
                    newE.setEvaluationoutputpath(new File(output_evals_path + "/" + formatName(ouput_evals_format,noExtension,current,experiment_name,gene_type) + ".txt"));
                }

                newE.setChromesize(chromosomeSize);
                newE.setNumgens(generations);
                newE.setPopsize(popSize);
                newE.setGeneType(gene_type);
                newE.setExperimentName(experiment_name);
                newE.setSearchType(search_type);
                newE.setFitnessBudget(fitness_budget);
                toDo.add(newE);
            }

        }
        return toDo;

    }

    private static String formatName(String format,String filename ){
        String ret = format.replace("%f",filename);

        DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        Date date = new Date();
        dateFormat.format(date);

        ret = ret.replace("%D",dateFormat.format(date));

        dateFormat = new SimpleDateFormat("HH");

        ret = ret.replace("%h",dateFormat.format(date));

        dateFormat = new SimpleDateFormat("mm");

        ret = ret.replace("%m",dateFormat.format(date));

        return ret;


    }

    private static String formatName(String format, String filename, int iteration, String exptname, String geneType){
        return formatName(format, filename, iteration, exptname).replace("%G", geneType);
    }

    private static String formatName(String format, String filename, int iteration, String exptname){
        return formatName(format,filename,iteration).replace("%N", exptname);
    }

    private static String formatName(String format, String filename, int iteration){
        return formatName(format,filename).replace("%n",Integer.toString(iteration));
    }

    public static ArrayList<Experiment> runList(String[] filenames) throws IOException {
        ArrayList<Experiment> ret =new ArrayList<Experiment>();

        for(String f : filenames){
            ret.addAll(run(new File(f)));
        }

        return ret;
    }

    public static ArrayList<Experiment> runList(File[] files) throws IOException{
        ArrayList<Experiment> ret =new ArrayList<Experiment>();

        for(File f : files){
            ret.addAll(run(f));
        }

        return ret;
    }

    public static void usage(){
        System.out.println("Usage: RunFile <experiment.properties>"
                + "\n\twhere experiment.properties is the path to a properties file describing the experiment.");
    }
    public static void main(String[] args) throws IOException, ExperimentNotRunException{
	if(args.length == 0){
            usage();
            System.exit(-1);
        }	
        
	LogManager logMan=LogManager.getLogManager();
	logMan.readConfiguration(Thread.currentThread().getClass().getResourceAsStream("/logging.properties"));
		
        ArrayList<Experiment> toDo = runList(args);
        String name = toDo.get(0).getExperimentName();
        ExperimentRunner runner = new ExperimentRunner();
        Logger.getLogger(RunFile.class.getName()).log(Level.INFO, String.format( "[%s] Starting clustering of %d files",name, toDo.size()));
        Queue<Experiment> td = new LinkedList<Experiment>(toDo);

        long start = System.currentTimeMillis();
        ArrayList<Experiment> results = runner.runExperiments(td);
        Logger.getLogger(RunFile.class.getName()).log(Level.INFO, String.format("[%s] Clustering complete, total time: %d ms.",name, (System.currentTimeMillis() - start) ));
        for(Experiment r : results) r.storeAll();
        //TODO do some summary.
        System.exit(0);
    }
}
